using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

namespace DocumentCreationSystem.Services
{
    /// <summary>
    /// 管理本地 RWKV-Runner 进程的启动/停止与输出日志
    /// </summary>
    public class RWKVRunnerProcessService
    {
        private readonly ILogger<RWKVRunnerProcessService> _logger;
        private readonly HttpClient _httpClient;
        private Process? _proc;
        private readonly ConcurrentQueue<string> _log = new();
        private const int MaxLogLines = 1000;

        public event Action<string>? OnOutput;

        public RWKVRunnerProcessService(ILogger<RWKVRunnerProcessService> logger, HttpClient httpClient)
        {
            _logger = logger;
            _httpClient = httpClient;
            _httpClient.Timeout = TimeSpan.FromSeconds(10);
        }

        public bool IsRunning => _proc != null && !_proc.HasExited;

        public string[] GetRecentLogs(int max = 200)
        {
            return _log.ToArray();
        }

        public async Task<bool> StartAsync(string exePath, string args, int port, CancellationToken ct)
        {
            if (string.IsNullOrWhiteSpace(exePath) || !File.Exists(exePath))
                throw new FileNotFoundException("找不到 RWKV-Runner 可执行文件", exePath);
            if (IsRunning) return true;

            // 支持 {port} 占位符
            args = (args ?? string.Empty).Replace("{port}", port.ToString());

            var psi = new ProcessStartInfo
            {
                FileName = exePath,
                Arguments = args,
                UseShellExecute = false,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                CreateNoWindow = true,
            };

            var proc = new Process { StartInfo = psi, EnableRaisingEvents = true };
            proc.OutputDataReceived += (s, e) => { if (!string.IsNullOrEmpty(e.Data)) EnqueueLog(e.Data); };
            proc.ErrorDataReceived += (s, e) => { if (!string.IsNullOrEmpty(e.Data)) EnqueueLog("[ERR] " + e.Data); };
            proc.Exited += (s, e) => EnqueueLog("[Runner] 进程已退出");

            _ = Task.Run(() =>
            {
                try
                {
                    if (proc.Start())
                    {
                        _proc = proc;
                        proc.BeginOutputReadLine();
                        proc.BeginErrorReadLine();
                        EnqueueLog($"[Runner] 已启动: {exePath} {args}");
                    }
                    else
                    {
                        EnqueueLog("[Runner] 启动失败");
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "启动 RWKV-Runner 失败");
                    EnqueueLog("[Runner] 启动异常: " + ex.Message);
                }
            }, ct);

            // 简单等待以便健康检查生效
            await Task.Delay(1000, ct);
            return true;
        }

        public void Stop()
        {
            try
            {
                if (_proc != null && !_proc.HasExited)
                {
                    _proc.Kill(entireProcessTree: true);
                    _proc.Dispose();
                }
                _proc = null;
                EnqueueLog("[Runner] 已停止");
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "停止 RWKV-Runner 异常");
            }
        }

        public async Task<bool> HealthCheckAsync(string baseUrl, int retry = 5, int delayMs = 1000)
        {
            for (int i = 0; i < retry; i++)
            {
                try
                {
                    using var req = new HttpRequestMessage(HttpMethod.Get, Combine(baseUrl, "/v1/models"));
                    using var resp = await _httpClient.SendAsync(req);
                    if (resp.IsSuccessStatusCode) return true;
                }
                catch
                {
                    // ignore
                }
                await Task.Delay(delayMs);
            }
            return false;
        }

        private static string Combine(string a, string b)
        {
            if (a.EndsWith("/")) a = a.TrimEnd('/');
            return a + b;
        }

        private void EnqueueLog(string line)
        {
            _log.Enqueue(line);
            OnOutput?.Invoke(line);
            while (_log.Count > MaxLogLines && _log.TryDequeue(out _)) { }
        }
    }
}

